#include "ip.h"
#include "icmp.h"
#include "arpcache.h"
#include "rtable.h"
#include "arp.h"
#include "log.h"
#include <stdlib.h>
#include <assert.h>

// initialize ip header 
void ip_init_hdr(struct iphdr *ip, u32 saddr, u32 daddr, u16 len, u8 proto)
{
	ip->version = 4;
	ip->ihl = 5;
	ip->tos = 0;
	ip->tot_len = htons(len);
	ip->id = rand();
	ip->frag_off = htons(IP_DF);
	ip->ttl = DEFAULT_TTL;
	ip->protocol = proto;
	ip->saddr = htonl(saddr);
	ip->daddr = htonl(daddr);
	ip->checksum = ip_checksum(ip);
}

// lookup in the routing table, to find the entry with the same and longest prefix
// The input address is in host byte order.
// Tips:
// rtable is a global variable defined in rtable.c
rt_entry_t *longest_prefix_match(u32 dst) {
    rt_entry_t *best_match = NULL;  // 最佳匹配条目
    u32 longest_mask = 0;          // 当前最长的匹配掩码

    // 遍历路由表
    rt_entry_t *entry = NULL;
	list_for_each_entry(entry, &rtable, list) {
        // 检查是否匹配
        if ((dst & entry->mask) == (entry->dest & entry->mask)) {
            // 如果匹配并且掩码长度更长，则更新最佳匹配
            if (entry->mask > longest_mask) {
                longest_mask = entry->mask;
                best_match = entry;
            }
        }
    }

    return best_match;  // 返回最佳匹配条目
}

// different from function ip_forward_packet, ip_send_packet sends packet generated by router itself
// This function is used to send icmp packets.
// You can complete this function after completing icmp_send_packet, or directly integrate the function into icmp_send_packet.
// The interface to send the packet is specified by longest_prefix_match.
void ip_send_packet(char *packet, int len)
{
	// assert(0 && "TODO: function ip_send_packet not implemented!");

    // icmp checksum already updated in icmp_send_packet;

    struct iphdr *ip=packet_to_ip_hdr(packet);
    rt_entry_t *rt_entry=NULL;

    log(DEBUG,"Finding ip by longest prefix match: " IP_FMT,NET_IP_FMT_STR(ip->daddr) );
    rt_entry=longest_prefix_match((ntohl)(ip->daddr));
    
    if(!rt_entry){
        log(ERROR,"fail to find longest prefix match entry in arp table");
        return;
    }

    // Find the interface
    iface_info_t *iface = rt_entry->iface;

    struct icmphdr *icmp_hdr = (struct icmphdr *)IP_DATA(ip);

    if(icmp_hdr->type==ICMP_DEST_UNREACH || icmp_hdr->type==ICMP_TIME_EXCEEDED){
        log(DEBUG,"unreachable packet or time exceed packet, update eh header shost to iface->MAC " ETHER_STRING,ETHER_FMT(iface->mac));
        struct ether_header *eh=(struct ether_header*)(packet);
        memcpy(eh->ether_shost,iface->mac,ETH_ALEN);
        ip->saddr=(htonl)(iface->ip);
        
        // update checksum
        ip->checksum=0;
        ip->checksum=ip_checksum(ip);

        int icmp_len = ICMP_HDR_SIZE + IP_BASE_HDR_SIZE + 8;

        icmp_hdr->checksum=0;
        icmp_hdr->checksum=icmp_checksum(icmp_hdr,icmp_len);
    }
    

   

    // Set next hop
    u32 next_hop = rt_entry->gw ? rt_entry->gw : ntohl(ip->daddr);
    log(DEBUG,"Found. next hop is " IP_FMT,HOST_IP_FMT_STR(next_hop));
    // next hop is a host ordered
    log(DEBUG,"sending the ip packet");
    iface_send_packet_by_arp(iface, next_hop, packet, len);
}
